#include <assert.h>
#include <stdio.h>

#include "../src/estep.c"

typedef struct _EstepTestDatum EstepTestDatum ;
struct _EstepTestDatum 
{ 
  struct
    {
      double    priorz[5];
      Rating    ratings[8];
      double    alpha[4 * 5 * 5];
      double    logbeta[3];
    }                   inputs;

  double pz[3*5];
};

static void
test_estep (void)
{
  EstepTestDatum data[] = 
    {{{{0.788009, 0.996442, 0.82066, 0.00248895,  0.906416}, {{0, 0, 0}, {3, 1, 4}, {1, 2, 0}, {2, 2, 0}, {0, 1,  2}, {3, 0, 2}, {2, 1, 0}, {0, 0, 4}}, {0, 0.613471, 0.749282,  0.654335, 0.529157, 0.212059, 0, 0.351128, 0.826862, 0.432094,  0.245815, 0.460571, 0, 0.709696, 0.664574, 0.885258, 0.550973,  0.349347, 0, 0.863038, 0.857708, 0.567922, 0.237935, 0.0750297, 0,  0, 0.861266, 0.747262, 0.235446, 0.168613, 0.247795, 0, 0.99798,  0.581111, 0.639457, 0.0357365, 0.646851, 0, 0.754248, 0.207363,  0.789921, 0.18628, 0.0445519, 0, 0.542788, 0.904663, 0.635307,  0.695205, 0.67975, 0, 0, 0.0469554, 0.0673858, 0.45727, 0.60472,  0.185689, 0, 0.320124, 0.221824, 0.436107, 0.937894, 0.322144, 0,  0.640713, 0.79665, 0.902157, 0.675292, 0.886465, 0, 0.589288,  0.112236, 0.489012, 0.841913, 0.0464992, 0, 0, 0.207573, 0.853705,  0.146709, 0.366749, 0.160617, 0, 0.786319, 0.689439, 0.762029,  0.974928, 0.466195, 0, 0.467615, 0.325922, 0.0370341, 0.144051,  0.826901, 0, 0.529272, 0.134877, 0.468759, 0.940436, 0.939984,  0}, {0.0226406, 0.979747, 0.0985229}}, {0.235047, 0.246098,  0.374269, 0.000216508, 0.144369, 0.0903313, 0.0753046, 0.12205,  0.0000894175, 0.712225, 0.356363, 0.313366, 0.158949, 0.000232553,  0.17109}}, {{{0.893485, 0.815068, 0.126042, 0.951814,  0.526736}, {{1, 1, 3}, {0, 1, 4}, {0, 0, 1}, {2, 1, 4}, {3, 2,  3}, {3, 1, 1}, {1, 1, 2}, {1, 2, 1}}, {0, 0.65445, 0.339723,  0.262375, 0.764707, 0.679522, 0, 0.873528, 0.79476, 0.438785,  0.642488, 0.729476, 0, 0.967859, 0.909513, 0.507612, 0.260717,  0.0274229, 0, 0.969528, 0.484971, 0.28097, 0.9289, 0.0760433, 0,  0, 0.669903, 0.154928, 0.977086, 0.549307, 0.0154528, 0, 0.815205,  0.71471, 0.784601, 0.33593, 0.941678, 0, 0.91995, 0.345816,  0.693442, 0.212201, 0.952091, 0, 0.436303, 0.185831, 0.951484,  0.924668, 0.466775, 0, 0, 0.70086, 0.670514, 0.995768, 0.390732,  0.0309563, 0, 0.515586, 0.0186823, 0.841424, 0.0155035, 0.70038,  0, 0.303972, 0.0568236, 0.679573, 0.758703, 0.384022, 0, 0.711007,  0.986131, 0.546502, 0.431931, 0.274704, 0, 0, 0.8003, 0.595017,  0.507263, 0.807929, 0.0994407, 0, 0.924503, 0.511495, 0.417197,  0.0684844, 0.408918, 0, 0.492813, 0.575773, 0.052981, 0.708537,  0.188841, 0, 0.518949, 0.373408, 0.949834, 0.804819, 0.807942,  0}, {0.387277, 0.403333, 0.372888}}, {0.149702, 0.432286,  0.0253424, 0.253961, 0.138709, 0.239434, 0.203314, 0.0781861,  0.1057, 0.373366, 0.158782, 0.295884, 0.0120604, 0.490681,  0.0425928}}, {{{0.533238, 0.586977, 0.808316, 0.865625,  0.725309}, {{1, 1, 3}, {1, 0, 4}, {2, 1, 0}, {3, 0, 4}, {1, 0,  4}, {2, 1, 1}, {3, 0, 2}, {1, 1, 4}}, {0, 0.487536, 0.883812,  0.35413, 0.308112, 0.419052, 0, 0.474894, 0.861317, 0.732339,  0.366071, 0.766357, 0, 0.672476, 0.213389, 0.992663, 0.816523,  0.867658, 0, 0.405447, 0.605386, 0.41319, 0.49477, 0.872209, 0, 0,  0.0184088, 0.604874, 0.629145, 0.1469, 0.530873, 0, 0.721062,  0.275015, 0.838789, 0.111821, 0.246168, 0, 0.413698, 0.10645,  0.745751, 0.479811, 0.741221, 0, 0.893061, 0.753088, 0.663288,  0.873564, 0.487613, 0, 0, 0.147702, 0.250098, 0.378794, 0.615404,  0.129293, 0, 0.645224, 0.749649, 0.468504, 0.598421, 0.924162, 0,  0.474634, 0.629715, 0.486599, 0.677994, 0.0609363, 0, 0.523265,  0.740849, 0.198183, 0.319715, 0.630204, 0, 0, 0.987761, 0.534895,  0.446151, 0.142591, 0.840059, 0, 0.284797, 0.0673567, 0.527186,  0.710765, 0.639573, 0, 0.317707, 0.0586829, 0.112345, 0.715411,  0.843073, 0, 0.428968, 0.625745, 0.0374166, 0.782137, 0.905703,  0}, {0.884896, 0.839233, 0.462422}}, {0.0775089, 0.003616,  0.406673, 0.00248252, 0.509719, 0.205734, 0.322238, 0.0433373,  0.0859086, 0.342782, 0.151511, 0.16678, 0.22967, 0.245954,  0.206085}}};

  for (unsigned int i = 0; i < sizeof (data) / sizeof (data[0]); ++i)
    {
      double pz[3*5];

      for (unsigned int j = 0; j < 3*5; ++j) { pz[j] = drand48 (); }

      estep (data[i].inputs.alpha,
             5,
             data[i].inputs.logbeta,
             3,
             pz,
             data[i].inputs.priorz,
             data[i].inputs.ratings,
             8,
             0);

      for (unsigned int j = 0; j < 3*5; ++j)
        {
          assert (fabs (pz[j] - data[i].pz[j]) <=
                  1e-4 * (1 + fabs (pz[j]) + fabs (data[i].pz[j])) ||
                  (fprintf (stderr, "%u: %g ?= %g\n", j, pz[j], data[i].pz[j]), 0));
        }
    }
}

static void
test_estep_symmetric (void)
{
  EstepTestDatum data[] = 
    {{{{0.510627, 0.0281321, 0.94178, 0.563479,  0.981261}, {{1, 0, 0}, {0, 0, 4}, {2, 0, 4}, {0, 2, 4}, {1, 2,  2}, {0, 1, 2}, {2, 2, 1}, {0, 0, 2}}, {0, 0.162404, 0.00241516,  0.214978, 0.724963, 0.549392, 0, 0.103638, 0.637123, 0.653976,  0.0922644, 0.268683, 0, 0.948562, 0.984851, 0.368684, 0.827192,  0.927177, 0, 0.183744, 0.692775, 0.979462, 0.605781, 0.673117, 0,  0, 0.664643, 0.0376818, 0.042302, 0.691856, 0.502239, 0,  0.0352666, 0.827324, 0.966893, 0.952847, 0.931629, 0, 0.190201,  0.312917, 0.860582, 0.662946, 0.241639, 0, 0.328066, 0.491898,  0.835754, 0.314462, 0.144322, 0, 0, 0.799123, 0.856292, 0.70868,  0.471205, 0.13448, 0, 0.818611, 0.666378, 0.779349, 0.632241,  0.783344, 0, 0.839054, 0.812457, 0.679395, 0.851715, 0.648854, 0,  0.49954, 0.818812, 0.188769, 0.407215, 0.171474, 0, 0, 0.326914,  0.353014, 0.0927531, 0.0271521, 0.527791, 0, 0.496722, 0.384073,  0.555947, 0.393311, 0.678111, 0, 0.717694, 0.776597, 0.761069,  0.894767, 0.87864, 0, 0.964141, 0.0816747, 0.0430521, 0.229786,  0.464601, 0}, {0.262862, 0.854284, 0.822571}}, {0.193824,  0.0047156, 0.196377, 0.164992, 0.440091, 0.27233, 0.0140954,  0.586008, 0.0435947, 0.0839724, 0.0512538, 0.0357267, 0.0596055,  0.246749,  0.606665}}, {{{0.293126, 0.935948, 0.501269, 0.729818,  0.265974}, {{1, 0, 3}, {1, 0, 4}, {1, 0, 2}, {1, 1, 3}, {2, 0,  4}, {2, 2, 4}, {3, 1, 1}, {3, 0, 1}}, {0, 0.408157, 0.0045476,  0.345745, 0.710027, 0.0148469, 0, 0.326437, 0.628051, 0.93343,  0.253778, 0.431669, 0, 0.749411, 0.969289, 0.172103, 0.388617,  0.519625, 0, 0.504689, 0.909241, 0.534334, 0.697054, 0.211563, 0,  0, 0.973292, 0.0330644, 0.967236, 0.945588, 0.565135, 0,  0.0285168, 0.621491, 0.235561, 0.550288, 0.70208, 0, 0.99344,  0.302131, 0.29651, 0.270411, 0.244029, 0, 0.332842, 0.124407,  0.881793, 0.724403, 0.828153, 0, 0, 0.215166, 0.34746, 0.0273489,  0.61659, 0.241874, 0, 0.314395, 0.0601125, 0.671002, 0.67674,  0.285879, 0, 0.438621, 0.435441, 0.126452, 0.583798, 0.445181, 0,  0.13331, 0.829942, 0.313387, 0.201152, 0.800468, 0, 0, 0.705535,  0.431594, 0.476749, 0.972316, 0.490368, 0, 0.0841342, 0.4494,  0.355726, 0.248494, 0.769739, 0, 0.389288, 0.684724, 0.571754,  0.48386, 0.950666, 0, 0.249283, 0.445303, 0.900062, 0.505485,  0.115973, 0}, {0.615361, 0.586675, 0.304333}}, {0.00962582,  0.27809, 0.0651666, 0.275532, 0.371585, 0.0252244, 0.319233,  0.0643486, 0.49165, 0.0995433, 0.0762703, 0.228889, 0.18572,  0.322562,  0.186559}}, {{{0.315505, 0.909826, 0.155081, 0.827584,  0.343189}, {{1, 2, 4}, {0, 2, 2}, {3, 2, 0}, {1, 2, 2}, {3, 1,  3}, {0, 1, 2}, {1, 2, 0}, {3, 0, 0}}, {0, 0.419457, 0.0709465,  0.378184, 0.987463, 0.170963, 0, 0.301208, 0.988896, 0.302739,  0.599209, 0.817347, 0, 0.0382301, 0.0534564, 0.153906, 0.917285,  0.532745, 0, 0.937483, 0.538546, 0.33061, 0.228412, 0.621979, 0,  0, 0.62872, 0.17553, 0.400828, 0.27879, 0.209263, 0, 0.104583,  0.0226439, 0.291327, 0.0382995, 0.803376, 0, 0.0337475, 0.988588,  0.439091, 0.986029, 0.995517, 0, 0.935131, 0.285185, 0.0687435,  0.462773, 0.997648, 0, 0, 0.746639, 0.738133, 0.234361, 0.375669,  0.117919, 0, 0.562603, 0.833533, 0.0968788, 0.908656, 0.45802, 0,  0.810889, 0.805552, 0.870357, 0.654644, 0.777141, 0, 0.816964,  0.431266, 0.668616, 0.781624, 0.881833, 0, 0, 0.146082, 0.599872,  0.318851, 0.884185, 0.399443, 0, 0.861739, 0.0844906, 0.508517,  0.281524, 0.299136, 0, 0.250958, 0.411638, 0.372867, 0.841116,  0.440069, 0, 0.606086, 0.502511, 0.186472, 0.662928, 0.789122,  0}, {0.0712445, 0.517856, 0.881304}}, {0.175954, 0.402828,  0.0474467, 0.291518, 0.0822535, 0.10526, 0.293381, 0.0500868,  0.460025, 0.0912479, 0.783607, 0.114356, 0.00725769, 0.0561511,  0.0386282}}};

  for (unsigned int i = 0; i < sizeof (data) / sizeof (data[0]); ++i)
    {
      double pz[3*5];

      for (unsigned int j = 0; j < 3*5; ++j) { pz[j] = drand48 (); }

      estep (data[i].inputs.alpha,
             5,
             data[i].inputs.logbeta,
             3,
             pz,
             data[i].inputs.priorz,
             data[i].inputs.ratings,
             8,
             1);

      for (unsigned int j = 0; j < 3*5; ++j)
        {
          assert (fabs (pz[j] - data[i].pz[j]) <=
                  1e-4 * (1 + fabs (pz[j]) + fabs (data[i].pz[j])) ||
                  (fprintf (stderr, "%u: %g ?= %g\n", j, pz[j], data[i].pz[j]), 0));
        }
    }
}

int 
main (void)
{
  srand (69);

  test_estep ();
  test_estep_symmetric ();

  /* TODO: copy longer test vectors in */

  return 0;
}
